承接著之前單元測試相關的文章中,提到了程式的可測試性為系統重要的品質指標之一。這篇文章要提的,則是也很常用來當作檢視測試的品質指標之一。
測試不是有做就好,做心安的...透過code coverage,可以快速的得知,目前系統中,有多少程式被測試過,以及測試的過程中,程式執行的路徑為何。
[如何提升系統品質]系列文章連結
Code Coverage的定義
基本上就是測試程式碼的涵蓋範圍,不考量成本跟投資效益比,涵蓋率越高,當然代表品質提升面也會比較廣。
Code Coverage的種類
Code Coverage種類不少,以下舉出幾種:
1.function coverage:
以function為單位,檢測有哪些function曾經被測試程式測試過。只要有測試到該function,就當作測過了。假設production code裡面有10個function,只有1個被測試過,那function coverage就是10%。
2.Line Coverage:
也就是是否production code的每一行程式碼,都有被測試程式執行到。也是一般.NET的code coverage工具裡面,最常看到的涵蓋率。
如果,你以為每一行都有跑到,Line Coverage 100%就代表這是測試品質的最高表現,那就錯了,Line Coverage只是幾個涵蓋率分類裡面的子集合而已。
3.Statement Coverage:
與line Coverage一行一行用斷行符號來分的切入角度不同,而是使用statement來當檢測單位。在許多的程式語言中,是允許一行裡面出現多個statement的,當一行裡面擁有多個statement,而測試程式中,只有檢測到其中一個statement,那麼line coverage會涵蓋到,statement coverage卻可以抓出其中涵蓋度不足的部分。
4.Decision Coverage:
就像決策樹一般,每一個決策分歧點,也就是程式執行路徑,為檢測單位。值得一提的是,Decision coverage針對的是分歧後的結果。然而,Decision Coverage可以涵蓋到Statement coverage的死角,舉個例子:
public double Sample(double a, double b, double x)
{
if (a > 0 && b == 0)
{
x = x / a;
}
if (a == 2 || x == 1)
{
x = x / b;
}
return x;
}
statement coverage要100%,test case只需要a=2, b=0, x=0,即每一個statement都會跑到。但一樣每一個statement都測到了,就代表這個測試完美了嗎?no, 因為判斷式的條件有多個,判斷式裡面的陳述式所回傳的bool值,可能發生意想不到的錯誤。
5.Condition Coverage:
即使是Decision Coverage,也有不足之處。不足處在於Decision針對的是每條不同路徑要測試的程式主體,但判斷式本身,卻不過問。而Condition Coverage是連判斷式裡面的陳述式(sub-expression),每一種陳述式所回傳的bool正反兩種情境都要測到。
以wiki上的例子來說:
if a and b then
只要測試
a=true, b=false
a=false, b=true
就能滿足Condition Coverage。
6.Condition/Decision Coverage:
沒錯!就是摻在一起做撒尿牛丸。除了判斷式中每個陳述式的bool都要測true/false,也要測試各種路徑裡面的各種結果。
7.Modified condition/decision coverage:
可以說是Condition/Decision Coverage的完成體,一樣以wiki上的例子來說:
if (a or b) and c then
要滿足Condition Coverage (Decision就只是測試判斷式的大括號肚子裡面的內容,這邊先略過),基本上只要測試
a=true, b=true, c=true
a=false, b=false, c=false
就是三種陳述式的true/false都測到了。
但,有測試到最後組合出來的結果嗎?
以這個例子來說,整個if陳述式由兩個子陳述式所組成,一個是 (a or b),一個是c。
先看第一個(a or b),要測試整個陳述式結果為true跟false,而(a or b)又有a與b兩個子陳述式。
所以,要滿足各種可能,則需測試:
a=false, b=false, c=true => 第一個陳述式為false,第二個陳述式為true,and結果為false
a=true, b=false, c=true => 第一個陳述式為true,第二個陳述式為true,and結果為true
a=false, b=true, c=true => 第一個陳述式為true,第二個陳述式為true,and結果為true
a=true, b=true, c=false => 第一個陳述式為true,第二個陳述式為false,and結果為false
也就是組合條件,去測到每一個子陳述式的true/false。
8.Multiple Condition Coverage:
最後當然就是最瘋狂的一種,每一個陳述式都有true/false,去做2的n次方種組合。
以上面的例子就是:
a=false, b=false, c=false
a=false, b=false, c=true
a=false, b=true, c=false
a=false, b=true, c=true
a=true, b=false, c=false
a=true, b=false, c=true
a=true, b=true, c=false
a=true, b=true, c=true
Code Coverage的工具
很抱歉的是,我知道的都是需要收費的。一個open source不用收費的PartCover(簡介),作者沒有再維護了。(這就是open source現實的地方)
其他收費的工具,請參考wiki上的介紹:
Tools for C# .NET
DevPartner
JetBrains dotCover
Kalistick
NCover
TestDriven.NET
Visual Studio 2010
補充工具
Microsoft Research有出一個很棒的東西叫做Pex and Moles,可以讓你很『快速』的產生高程式碼覆蓋率的單元測試程式,且支援多種Unit Test Framework。這邊的快速,指的是可以省去我們思考不同的test case滿足不同路徑的時間,但Pex還是需要時間、CPU去計算出滿足所有路徑的input/output。
針對重要的Scenario,可以透過它來補足測試遺漏的地方。
它也有個有趣的網頁,是可以讓我們寫程式,讓Pex來跑所有可能性,請參考:PexForFun
與VS2010整合得很好,最上面的例子,跑完Pex parse出來的結果會像下圖這樣:
產生的單元測試碼,請見下圖
自動產生的test case,請見下圖
結論
Code Coverage的迷思
上面介紹了這麼多種Code Coverage的分類,幾乎是一個比一個還嚴苛,您覺得呢?我只能說,這太遙不可及了,多少人連單元測試都還沒寫過。越往下的測試分類所花的成本越高,投資效益比越低。
即使只有line coverage,我也認為足夠用了。什麼叫做足夠用,除非是寫一個完完全全不容許任何差錯,沒有bug,沒有絲毫遺漏的系統。不然,系統多少都有bug,多少都得fix bug。NASA發射太空梭都有失敗的了,何況我們的程式。
請把code coverage的數字當作一個健康指標 (甚至稱不上系統品質指標,勉強稱得上是測試的品質指標),用來檢查什麼?
1.有沒有做單元測試?
2.重要的Scenario有沒有涵蓋多一點情境?
3.現在有哪些程式碼沒有被測過?
4.發生測試失敗時,失敗的原因點production code是否有被test case涵蓋到。
夠了,這樣就很夠了。
請把更多的時間,花在如何寫出具有可測試性的production code,如何落實Unit Test,以及如何用最少的成本,最低的阻力建立自動化的環境。重點在於,如何讓每一次的重構、每一次的需求異動、每一次的bug,都能快速且準確的增加對應的Unit Test,才是要中之要。
Reference
1.http://en.wikipedia.org/wiki/Code_coverage
2.http://blog.udn.com/chungchia/3341216
3.http://www.51testing.com/?uid-3733-action-spacelist-type-blog-itemtypeid-9157,動態圖片解說很讚